test: verify Telegram successor against current dev - #11
Draft
twoimo wants to merge 17 commits into
Draft
Conversation
…ispose flake (Yeachan-Heo#3144) * test(sdk): make teardown ordering witness production-observable The Phase 2 rewrite of "session teardown drains admitted direct gate resolution" was accepted with a narrowed claim because a mutation probe survived: replacing `await rt.waitForGateResolutionQuiescence()` with `void ...` still passed. The test fully mocked `resolveGate`, so the delayed operation never touched the real terminal controller, and it released the resolver after a single setImmediate while `stopSession()` was independently awaiting the native `pushFrameAndWait(session_closed)` barrier — resolution therefore completed before teardown reached detachment even without the quiescence await. Now the test calls through the original `registerGateTerminalController` and the original `resolveGate` (wrapping the latter only with a deferred pre-terminalization gate), makes `pushFrameAndWait(session_closed)` an explicit test-controlled pre-drain barrier, asserts the controller is still attached at the quiescence point, then observes real accepted terminalization and gate continuation before detachment. Mutation-proved: the void-await mutation now FAILS at the pre-detachment assertion (controllerAttached false). Passes 10/10 unmutated. * test(runtime): poll for the TERM marker in the concurrent-dispose redteam Caught live by the stabilization soak: main-nontag rehearsal run 30149261910 failed on shard 11 with (fail) process-lifecycle adversarial owned-process invariants > double and concurrent dispose share one settled result and issue one terminating signal expect(received).toHaveLength(expected) Expected: 1 Received: 0 The child's TERM trap appends its marker asynchronously (`trap 'echo term >> $tmp; exit 0' TERM`), so under shard load `awaitExit` can return before that write lands and the single-sample read observes an empty file. The file already has a `waitForAsync` helper for exactly this shape; the marker assertion just wasn't using it. Polls for the single terminating signal before asserting, preserving the original invariant (exactly one `term` line — not "at least one"). Verified: 15/15 reruns, 4x parallel contention clean, whole file 9/9, typecheck and biome clean. This test was NOT in the 62-suspect audit shortlist: it did not fail or retry during the mined two-week window, so it is a genuinely new observation the soak surfaced. --------- Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
`sh` runs a TERM trap only after the current foreground command returns. With the child looping on `sleep 1` and `gracefulMs: 500`, dispose could escalate to SIGKILL before the handler wrote its `term` marker, so the one-terminating-signal assertion saw an empty file (observed in CI run 30149261910, coding-agent shard 11). Shorten the loop interval to 0.05s and use the module's own `DEFAULT_GRACEFUL_MS` (2000ms) so the trap has a deterministic window. Under 18-worker CPU contention the old shape wins the race 8/20; the new shape wins 20/20. All assertions are unchanged. Also record the missing `## [Unreleased]` changelog entries for Yeachan-Heo#3109, Yeachan-Heo#3127, and Yeachan-Heo#3131, found while auditing release scope after v0.11.9.
…turns (Yeachan-Heo#3147) Lifecycle `session_shutdown` started `controller.stopCurrentSession(ctx)` but discarded the promise, awaiting only `stopSession(id)`. Once startup has settled the host is broker-visible and can accept `session.close` while the startup handler's post-start `reconcileCurrentSession` is still running. That reconciliation can mint a replacement notification-root token; `ensureTelegramDaemon` then unregisters it asynchronously. Shutdown could therefore return — and disposal exit — before that unregister's file lock and atomic registry write settled, leaving a stale `sessions[id]` row that the retained older token is correctly fenced from removing (`unregisterNotificationRoot` rejects token mismatches by design). Now shutdown snapshots `sessionStartPromises.has(id)` first and awaits the settled controller stop after `stopSession` whenever startup was NOT pending, so completed-start reconciliation and its replacement-token cleanup are joined. The intentional nonblocking path is preserved exactly where it matters: a genuinely pending startup entry (the `/notify on` case) still leaves the controller stop fire-and-forget. Surfaced by the flaky-CI stabilization soak. The regression test "Telegram root release failure is retained and retried through lifecycle shutdown" failed deterministically on darwin-arm64 at dev head while Linux CI stayed green (run 30147146988, 34/34 shards) — a completion- ordering divergence, not a `/var` canonicalization or native-addon issue: both registry and notification-root paths are lexical `path.join` with no realpath or case folding. Verified on darwin-arm64: the previously-failing test now passes 10/10, the whole sdk-host-wiring file is 72/72 (first fully green run of this file on Darwin), and telegram daemon + btw-e2e are 463/463. The assertion was kept intact rather than replaced with polling, which would have hidden the lifecycle-return bug. Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…eachan-Heo#3149) Caught by the stabilization soak: main-nontag rehearsal run 30151115867 failed on shard 8 with (fail) session_start swallows startup plus owner-release failure without surfacing an extension error expect(surfaced).toEqual([]) received 1 surfaced error The test used `mockRejectedValueOnce` on the SHARED prototypes `NotificationServer.prototype.start` and `SessionSdkHost.prototype.stop`. That is a one-shot global: a peer test scheduled concurrently in the same shard can consume the single rejection first. When that happens this test's own `start()` resolves, so `startSession` never reaches the `stopSession` catch that sets `suppressExtensionError` (src/sdk/bus/index.ts:4407-4420), and the startup error surfaces through `session_start` instead of being swallowed. Three separate tests in this file arm one-shot rejections on the same two prototypes, so the interference is cross-test within a shard rather than a missing restore — every one of them restores correctly. Replaces both one-shots with per-test guarded implementations that reject only on this test's first call and then delegate to the real method, preserving the existing assertions (including `expect(hostStop).toHaveBeenCalledTimes(2)`, which is what makes the "first call rejects" semantics load-bearing). Verified on darwin-arm64: 10/10 targeted reruns, 6x concurrent whole-file runs with zero failures, whole file 72/72, typecheck and biome clean. Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…#3139) (Yeachan-Heo#3146) The release CI default was narrowed to `contents: read` in Yeachan-Heo#3136 with `publish` retaining the sole job-level `contents: write` override, but no automated policy test protected that invariant. `dev-ci.yml` also had no explicit permission block and inherited the repository default. Add `scripts/check-workflow-permissions.ts`: a deterministic offline default-deny evaluator plus CLI that parses committed workflow YAML and requires every workflow to declare a least-privilege `permissions` block. Write scopes are denied everywhere except one explicit allowlist triple (ci.yml / publish / contents), and the three known workflows must declare exactly `contents: read`. Violations name workflow, job, permission path, actual, and expected. Codify `contents: read` on `dev-ci.yml` after auditing its jobs: they only check out, install, test, and exchange artifacts through the Actions artifact API, with no `git push`, `gh` CLI, `secrets.*`, or release/PR mutation, so no write-scoped token is required. Back it with real-YAML mutation proofs that read committed workflows from disk, mutate the parsed document, and re-evaluate -- covering workflow and job `write`, `write-all`, extra scopes, deleted permission blocks, and malformed job shapes. Route the regression through affected-path CI in both the broad and targeted planners so workflow and CI-harness changes select it alongside the existing release-policy suite, and pin the invariant in `release-policy.test.ts` as well. Refs Yeachan-Heo#3139 Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…/ resolver (Yeachan-Heo#3145) Closes non-blocking follow-up from the Yeachan-Heo#3080 post-merge review: cleanup_pending marker asymmetry between the async gate and the sync resolver. Reviewed exact head 90aa337 with durable MERGE_READY and green exact-head CI. Merged after post-merge Dev CI 30157160426 on 352a2c1 turned terminal green.
…o what shipped (Yeachan-Heo#3140) Documentation-only correction of readiness claims from Yeachan-Heo#3080. Exact-head MERGE_READY at 6cf2764 with green CI. Held during red/nonterminal post-merge gates; merged after Dev CI 30158413804 on 2317939 terminalized success.
…eachan-Heo#3132) `retryDelayMs()` honoured the server's `Retry-After` verbatim: if (Number.isFinite(seconds)) return Math.max(baseline, Math.max(0, seconds * 1000)); There was no upper bound, so a hostile or misconfigured endpoint could stall the usage fetch for as long as it liked. Verified: `Retry-After: 86400` made the provider sleep 86_400_000ms (24 hours), and the HTTP-date form did the same (86_399_386ms). The sleep is abort-aware, but with no signal — or a long-lived one — the usage refresh simply hangs, and auth-storage ranks credentials off this call. Every sibling path in the repo already bounds this: - utils/fetch-retry.ts DEFAULT_MAX_DELAY_MS = 60_000 (+ fail-fast above cap) - providers/openai-bounded-rate-limits.ts OPENAI_RETRY_DELAY_CAP_MS = 60_000 - session/agent-session.ts maxAcceptableDelayMs = 30_000 Only the Claude usage path was missing a ceiling. Fix: add MAX_RETRY_DELAY_MS = 60_000 and clamp through clampRetryDelay(), which also drops non-finite/negative hints. 60s matches the existing convention and preserves both pre-existing assertions exactly (`Retry-After: 1` → 1000, `Retry-After: 60` → 60_000). Regression coverage (test/claude-usage-retry.test.ts): absurd numeric Retry-After, absurd HTTP-date Retry-After, and a negative Retry-After. The first two fail against the unfixed code (received 86_400_000 / 86_399_386, expected 60_000). Gates: claude-usage-retry (11 pass), usage suite (28 pass across 5 files), tsc --noEmit clean. Co-authored-by: dmae97 <dmae97@users.noreply.github.com>
|
TELEGRAM_SUCCESSOR_CONFLICTS packages/coding-agent/src/sdk/bus/index.ts diff --cc packages/coding-agent/src/sdk/bus/index.ts
index b7d9432b,aa9aaea5..00000000
--- a/packages/coding-agent/src/sdk/bus/index.ts
+++ b/packages/coding-agent/src/sdk/bus/index.ts
@@@ -5172,22 -5235,10 +5235,26 @@@ export function createNotificationsExte
await Promise.allSettled([...branchStartupTasks]);
const id = sessionId(ctx);
const rt = runtimes.get(id);
++<<<<<<< ours
+ if (rt) terminalizeInFlightTools(rt, id, "unknown");
+ // Startup is only genuinely in flight when a `sessionStartPromises` entry
+ // exists. Once startup has settled, the host is broker-visible and its
+ // post-start `reconcileCurrentSession` may already have minted a
+ // replacement notification-root token whose unregister is still awaiting
+ // its file lock and atomic registry write. Returning before that settles
+ // leaves a stale `sessions[id]` row that the retained older token is
+ // correctly fenced from removing, so shutdown must join it.
+ const startupWasPending = sessionStartPromises.has(id);
++=======
+ if (rt) terminalizeInFlightTools(rt, id, "cancelled");
++>>>>>>> theirs
const controllerStop =
typeof ctx.sessionManager.getCwd === "function" ? controller.stopCurrentSession(ctx) : Promise.resolve(false);
- void controllerStop.catch(error => logger.warn(`notifications: controller shutdown failed: ${String(error)}`));
+ const settledControllerStop = controllerStop.catch(error => {
+ logger.warn(`notifications: controller shutdown failed: ${String(error)}`);
+ return false;
+ });
+ if (startupWasPending) void settledControllerStop;
try {
await stopSession(id);
} catch (error) {
|
Owner-approved MERGE_READY at exact head 4caf43c with green CI (31 checks). Merged after post-Yeachan-Heo#3132 Dev CI 30159910672 on 77c9a60 terminalized success. Yeachan-Heo#3134 became dirty and was skipped under one-at-a-time sequencing.
…tones (rebase of Yeachan-Heo#3069) (Yeachan-Heo#3150) * perf(coding-agent): skip lock acquisition for already-completed tombstones Rebase of Yeachan-Heo#3069 (exact head 2b58273) onto current dev. `reconcileManagedTombstones` acquired a durable fsync-backed operation lock for every tombstone in the scope, including tombstones whose targets had already published a `cleanup_completed` receipt and therefore had nothing left to reconcile. Scopes that accumulate many completed tombstones paid that cost on every `gjc resume` and delete. Skip the lock only when every target of the tombstone still satisfies the identity-bound `cleanupCompleted` predicate, which verifies schema version, state, scope digest, tombstone path, attempt, target path/sessionId/cwd, and the full identity tuple (dev, ino, size, mtimeNs, sha256). Any target that fails the predicate falls through to the existing locked reconciliation path unchanged, so this is never a blanket skip. Tests: - untouched completed receipts -> zero lock acquisitions - a completed receipt whose recorded target sha256 no longer matches -> lock is still acquired (negative control; fails if the skip is made unconditional) * ci: re-trigger PR checks after GitHub Actions internal job failures No product change. Previous Yeachan-Heo#3150 run failed with empty-step jobs annotated as "GitHub Actions has encountered an internal error when running your job". * ci: re-trigger PR checks after Actions recovery No product change. Previous re-trigger landed in the GHA internal-error window (empty-step jobs). Dev CI has since recovered on post-merge runs. --------- Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…3073) Owner-approved MERGE_READY at exact head ce4f799 with green CI (28 checks). Merged after post-Yeachan-Heo#3135/Yeachan-Heo#3150 sequencing: current free-merge after green on f14e244, with Yeachan-Heo#3150 already serial-merged to a11fd9f.
twoimo
force-pushed
the
successor/telegram-tool-activity-clean
branch
from
July 25, 2026 14:13
76e205b to
f14e244
Compare
github-actions
Bot
force-pushed
the
successor/telegram-tool-activity-clean
branch
from
July 25, 2026 14:21
591cc2c to
b58c66a
Compare
|
TELEGRAM_SUCCESSOR_VERIFIED |
Yeachan-Heo
pushed a commit
that referenced
this pull request
Aug 12, 2026
Codex P2 review on PR Yeachan-Heo#4206 surfaced seven genuine defects; all are documentation correctness fixes with no runtime impact: - mkdir -p ManualBackups before ditto (ditto cannot create missing parent) - invariant #11: SDK state lives in repo-local gitignored .gjc/state/, not 'outside the repository' -- the original wording contradicted the guide's own discovery path at the SDK section - model profile names: all four (frontier-heavy, gpt-heavy, glm-deepseek, kimi-gpt) are operator-defined, not just kimi-gpt; none ships with GJC - fixed-folder cd: single-quoted cd -- '<path>' suppresses tilde expansion; document that the plugin must normalize ~ to $HOME before shell-quoting - manifest path example: ~/src/gajae-code -> $HOME/src/gajae-code - verification --outfile=~/tmp -> --outfile="$HOME/tmp/..." (no tilde expansion after =) - SDK answer pad: clear only on action_resolved matching the displayed presentation id (matches telegram-reference.ts:547); reject controlled / multi-select asks before showing the fixed numeric pad Lore-id: 4206-review-fixforward Confidence: high Scope-risk: docs-only Tested: docs-index-lazy parity gate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Internal verification PR. Base mirrors upstream
devat 10a32cf; head contains only the Telegram successor diff and final rejected-terminal repair.